﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OpenTK;

namespace ArtemisTest
{
    //-----------------------------------------------//
    //  CLASS - ScriptReader.cs
    //-----------------------------------------------//
    //Global access
    //Reads data from txt files
    //and loads into a list in string arrays.
    public static class ScriptReader
    {
        public static string GetString(string path)
        {
            return File.ReadAllText(path);
        }

        public static List<string[]> GetData(string path)
        {
            List<string[]> resData = new List<string[]>();
            string[] data = File.ReadAllLines(path, Encoding.Default);
            foreach (string s in data)
            {
                string[] delimitedData = s.Split(new string[] { "\t", " ","/" }, 1000,
                    StringSplitOptions.RemoveEmptyEntries);

                if (s.Count() > 0 && s.ToArray()[0] != '#')
                {
                    resData.Add(delimitedData);
                }
            }
            return resData;
        }
    }

    public struct Face
    {
        public List<Vector3> _vert { get; set; }
        public List<Vector3> _norm {get;set;}
        public List<Vector3> _text {get;set;}

        public void AddVertex(Vector3 v)
        {
            if (_vert == null)
            {
                _vert = new List<Vector3>();
            } 
            _vert.Add(v);
        }
        public void AddNormal(Vector3 n)
        { 
            if (_norm == null) 
            { 
                _norm = new List<Vector3>(); 
            }
            _norm.Add(n); 
        }
        public void AddTexture(Vector3 t)
        {
            if (_text == null)
            {
                _text = new List<Vector3>();
            }
            _text.Add(t);
        }
    }

    public struct Material
    {
        public string path { get; set; }
        public string newmtl_Name { get; set; }
        public string map_Ka_Ambient{ get; set; }
        public string map_Kd_Diffuse{ get; set; }
        public int map_Ka_AmbientHandle { get; set; }
        public int map_Kd_DiffuseHandle { get; set; }
        public Vector3 Ka_Ambient { get; set; }
        public Vector3 Kd_Diffuse { get; set; }
        public Vector3 Ks_Specular { get; set; }
        public float Ns_Shininess { get; set; }
        public float d_Alpha { get; set; }

        public static Material LoadMaterial(string path, TextureSystem ts)
        {
            List<string[]> data = ScriptReader.GetData(path);
            Material mat = new Material();
            foreach (string[] row in data)
            {
                string ident = row[0];
                
                mat.path = path;

                switch (ident)
                {
                    case "newmtl" :
                        mat.newmtl_Name = row[1];
                        break;

                    case "Ns": 
                        mat.Ns_Shininess = (float)Convert.ToDouble(row[1]);
                        break;

                    case "d": 
                        mat.d_Alpha = (float)Convert.ToDouble(row[1]);
                        break;

                    case "Ka": mat.Ka_Ambient =
                         new Vector3
                         ((float)Convert.ToDouble(row[1]),
                         (float)Convert.ToDouble(row[2]),
                         (float)Convert.ToDouble(row[3]));
                        break;

                    case "Kd": mat.Kd_Diffuse =
                         new Vector3
                         ((float)Convert.ToDouble(row[1]),
                         (float)Convert.ToDouble(row[2]),
                         (float)Convert.ToDouble(row[3]));
                        break;

                    case "Ks": mat.Ks_Specular=
                         new Vector3
                         ((float)Convert.ToDouble(row[1]),
                         (float)Convert.ToDouble(row[2]),
                         (float)Convert.ToDouble(row[3]));
                        break;

                    case "map_Ka": 
                        mat.map_Ka_Ambient = "model\\"+row[1];
                        mat.map_Ka_AmbientHandle = 
                            ts.LoadBitmapToOpenGl(mat.map_Ka_Ambient);
                        break;

                    case "map_Kd":
                        mat.map_Kd_Diffuse = "model\\" + row[1];
                        mat.map_Kd_DiffuseHandle =
                            ts.LoadBitmapToOpenGl(mat.map_Kd_Diffuse);
                        break;
                }
            }
            return mat;
        }
    }

    public struct ObjModel
    {
        /// <summary>indices for each face in the form - v,tm,n,t,tn,n...*4</summary>
        public List<List<uint>> _ind { get; set; }
        public List<Vector3> _verts { get; set; }
        public List<Vector3> _norms { get; set; }
        public List<Vector3> _texts { get; set; }

        public List<Face> _faces { get; set; }
        public string _name { get; set; }

        public List<uint> _posInd { get; set; }
        public Material _material { get; set; }

        public void AddFace(Face f) 
        {
            if (_faces != null) 
            {
                _faces.Add(f); 
            } 
            else
            {
                _faces = new List<Face>();
                _faces.Add(f); 
            }
        }
    }

    public static class ModelReader
    {
        //public static Material LoadMaterial(string path, string name)
        //{

        //}

        public static ObjModel LoadModel(string path, TextureSystem ts)
        {
            ObjModel obj = new ObjModel();
            List<Vector3> verts = new List<Vector3>();
            List<Vector3> normals = new List<Vector3>();
            List<uint> posInd = new List<uint>();
            List<Vector3> textures = new List<Vector3>();
            List<string[]> data = ScriptReader.GetData(path);
            List<List<uint>> inds = new List<List<uint>>();

            foreach (string[] row in data)
            {
                if (row[0] == "usemtl")
                {
                     obj._material = Material.LoadMaterial(path.Substring(0, path.Length - 3) + "mtl",ts);
                }

                if (row[0] == "v")
                {
                    //vertices
                    float a = (float)Convert.ToDouble(row[1]);
                    float b = (float)Convert.ToDouble(row[2]);
                    float c = (float)Convert.ToDouble(row[3]);
                    verts.Add(new Vector3(a, b, c));//(new Vector3(a/5f, b/5f, c/5f +10));
                }
                if (row[0] == "vn")
                {
                    //normals
                    float a = (float)Convert.ToDouble(row[1]);
                    float b = (float)Convert.ToDouble(row[2]);
                    float c = (float)Convert.ToDouble(row[3]);
                    normals.Add(new Vector3(a, b, c));
                }
                if (row[0] == "vt")
                {
                    float a = (float)Convert.ToDouble(row[1]);
                    float b = (float)Convert.ToDouble(row[2]);
                    float c = (float)Convert.ToDouble(row[3]);
                    textures.Add(new Vector3(a, b, c));
                }
                if (row[0] == "g")
                {
                    //name
                    obj._name = row[1];
                }
                if (row[0] == "f")
                {
                    //mappings
                    List<uint> map = new List<uint>();
                    Face f = new Face();
                    int vertCount = (row.Count() - 1) / 3;
                    for (int i = 0; i < vertCount; i++)
                    {
                        int b = i * 3;

                        uint vertId = (uint)Convert.ToInt16(row[1 + b])-1;
                        uint textId = (uint)Convert.ToInt16(row[2 + b])-1;
                        uint normId = (uint)Convert.ToInt16(row[3 + b])-1;

                        posInd.Add(vertId);
                        map.Add(vertId);
                        map.Add(textId);
                        map.Add(normId);

                        f.AddVertex(verts[(int)vertId]);
                        f.AddTexture(textures[(int)textId]);
                        f.AddNormal(normals[(int)normId]);
                    }
                    inds.Add(map);
                    obj.AddFace(f);
                }
            }

            obj._posInd = posInd;
            obj._ind = inds;
            obj._verts = verts;
            obj._texts = textures;
            obj._norms = normals;


            return obj;
        }
    }

}
